package com.proxy;

import com.annotations.dao.*;
import com.pool.db.DBConnectionPool;
import com.utils.MyReflectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Objects;

/**
 * @author linyu.dai
 * @project rpc
 * @description Dao层sql代理
 * @creat 2022/12/1 11:23:44
 */
public class DaoInvocationHandle implements InvocationHandler {

    private Class<?> aClass;

    public DaoInvocationHandle(Class<?> aClass){
        this.aClass = aClass;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if(!aClass.isAnnotationPresent(Mapper.class) || !method.isAnnotationPresent(Queue.class)){
            throw new  RuntimeException("无法执行调用");
        }
        String sql = method.getAnnotation(Queue.class).sql();
        //获取连接对象 使用连接池
        Connection connection = DBConnectionPool.getConnections();
        PreparedStatement preparedStatement = connection.prepareStatement(sql);

        //获取返回值类型
        Class<?> returnType = method.getReturnType();
        //返回值
        Object o = returnType.getDeclaredConstructor().newInstance();
        //保存主键值
        Object id = null;

        if(args.length ==1 && !MyReflectUtils.isBasics(args[0].getClass())){
            Class<?> aClass1 = args[0].getClass();
            Field[] declaredFields = aClass1.getDeclaredFields();
            for (int i = 0; i < declaredFields.length; i++) {
                declaredFields[i].setAccessible(true);
                if(declaredFields[i].isAnnotationPresent(Id.class)) {
                    id = declaredFields[i].get(args[0]);
                }
                Object param = declaredFields[i].get(args[0]);
                preparedStatement.setObject(i+1, param);
            }
        }else {
            for (int i = 0; i < args.length; i++) {
                if(args[i].getClass().isAnnotationPresent(Id.class)){
                    id = args[i];
                };
                preparedStatement.setObject(i+1, args[i]);
            }
        }

        //不需要返回值
        if(returnType.equals(void.class)){
            preparedStatement.execute();
        }else{
            //需要返回值
            if(preparedStatement.execute()) {
                //有返回值 queue查询
                ResultSet resultSet = preparedStatement.getResultSet();
                set(resultSet, returnType, o);
            }else{
                //没有返回值 但是要返回值 那就去查询 id
                if(!returnType.isAnnotationPresent(Table.class) && Objects.isNull(id)){
                    throw new RuntimeException("无法查询");
                }
                String value = returnType.getAnnotation(Table.class).value();
                PreparedStatement preparedStatement1 = connection.prepareStatement("select * from "+value+" where id = ?");
                preparedStatement1.setObject(1, id);
                ResultSet resultSet = preparedStatement1.executeQuery();
                set(resultSet, returnType, o);
            }
        }
        DBConnectionPool.release(connection);
        return o;
    }

    /**
     * @Author linyu.dai
     * @Description 处理返回值
     * @Date 2022/12/1 15:36
     * @Param [resultSet, returnType, target]
     * @return void
     */
    private static void set(ResultSet resultSet, Class<?> returnType, Object target) throws SQLException, IllegalAccessException {
        while (resultSet.next()){
            Field[] declaredFields = returnType.getDeclaredFields();
            for (int i = 0; i < declaredFields.length; i++) {
                Field declaredField = declaredFields[i];
                String name = getName(declaredField);
                declaredField.setAccessible(true);
                Object value = resultSet.getObject(name, declaredField.getType());
                declaredField.set(target, value);
            }
        }
    }

    private static String getName(Field field){
        if(field.isAnnotationPresent(Column.class)){
            return  field.getAnnotation(Column.class).value();
        }else{
           return field.getName();
        }
    }

    /**
     * @Author linyu.dai
     * @Description 获取代理对象
     * @Date 2022/11/27 21:09
     * @Param []
     * @return java.lang.Object
     */
    public static Object getProxy(Class<?> aClass){
        return Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass}, new DaoInvocationHandle(aClass));
    }

}
